//
//  SimpleMapViewViewController.m
//  SimpleMapView
//
//  Created by Stefan Ivanov on 10/11/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//
//  Defaulft location is set to the SuperC building of RWTH.
//  As reference material I used videos from the iPhone programming lecture
//  that I watched during my summer break. Most importantly 10 Location and Maps.
//  The solution is using MapKit to get GoogleMaps map data and CoreLocation.
//  Both frameworks are supported in XCode for purposes like what is required by the asignment.

#import "RunnerController.h"

@implementation RunnerController

@synthesize mapView;
@synthesize locManager; 
@synthesize heading;
@synthesize currentLocation;
@synthesize bar;
@synthesize redView;
@synthesize isFirstLocationUpdate;
@synthesize isFirstMessageFromServer;
@synthesize availableCheckpoint;
@synthesize checkpointCaptureView;
@synthesize currentTeam = _currentTeam;

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //set initially to YES and after first message set to NO
    isFirstMessageFromServer = YES;
    
    //create and initialize location manager if it's not already done
    if (!self.locManager) {
        //save it in the property
        locManager = [[CLLocationManager alloc] init];
        locManager.delegate = self;
    }
    
    //start location services setting distance filter to whenever user moves and navigation accuracy
    locManager.distanceFilter = kCLDistanceFilterNone;
    locManager.desiredAccuracy = kCLLocationAccuracyBest;
    [locManager startUpdatingLocation];
    
    //start heading services setting heading filter in similar fashion as above
    if ([CLLocationManager headingAvailable]) {
        locManager.headingFilter = kCLHeadingFilterNone;
        [locManager startUpdatingHeading];
    }
    
    //disable panning in the mapview so it's always centered to oneself
    [mapView setScrollEnabled:NO];
    
    //make mapview show the user's location and the visual style to be standard
    mapView.showsUserLocation = NO;
    self.mapView.mapType = MKMapTypeStandard;
    
    //set the mapview delegate to self for the MKAnnotation callbacks to get called
    [mapView setDelegate:(id)self];
    
    UIPinchGestureRecognizer *zoomRecognizer = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handleZooming:)];
    [mapView addGestureRecognizer:zoomRecognizer];
    zoomRecognizer.delegate = self;
    [zoomRecognizer release];
    
    //create the bar, place it top of the screen and add it to the veiw
    bar = [[BarView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
    [self.view addSubview:bar];
    
    //Login to the server
    [[NetworkingCenter defaultServer] openConnectionToServer];
    //display aler view to notify user
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Connection opening..." message:@"Connection is being established" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
    
    [alertView show];
    [alertView release];
    
    //register for networking notifications
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(receiveNetworkingNotification:) 
                                                 name:NETWORKING_NOTIFICATION
                                               object:nil];
    
    [[NetworkingCenter defaultServer] sendMessage:[NetworkMessageCreator loginMessageWith:@"Stefan" role:ROLE_RUNNER team:self.currentTeam]];
    
    //!!!! TO BE DONE AFTER INFO IS GOTTEN FROM SERVER
    
    //initialize the number of collected points
    //self.bar.currentPoints = 1000;
    //initialize availableCheckpoints with random data 
    //this needs to be communicated by server or operator
    //self.availableCheckpoint = [[CheckPoint alloc] initWithCoordinate:CLLocationCoordinate2DMake(50.779512,6.058917) amountOfPoints:@"2000"];
    //!!!! TO BE DONE AFTER INFO IS GOTTEN FROM SERVER

}
- (void)dealloc
{
    [mapView release];
    [locManager release];
    [currentLocation release];
    [bar release];
    [checkpointCaptureView release];
    [availableCheckpoint release];
    [super dealloc];
}

- (void)didReceiveMemoryWarning
{
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidAppear:(BOOL)animated
{
    
    //if the view has just appeared then we set isFirstLocationUpdate variable
    isFirstLocationUpdate = YES;
}

-(void)viewDidDisappear:(BOOL)animated
{
    
    //if the view has disappeared then we set isFirstLocationUpdate variable
    isFirstLocationUpdate = NO;
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
    self.mapView = nil;
    [[NetworkingCenter defaultServer] sendMessage:[NetworkMessageCreator logoutMessage]];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    isFirstMessageFromServer = NO;

}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
    //update the location
    self.currentLocation = newLocation;
    
    if (isFirstLocationUpdate) {
        //center the mapview on the current location of the player and zoom to particular level 
        [self.mapView setRegion:MKCoordinateRegionMake(currentLocation.coordinate, MKCoordinateSpanMake(0.005, 0.005 )) animated:YES];
        isFirstLocationUpdate = NO;
    }
    
    //Keep sending the Network message, not neccssary to compare.
    //There is a timestamp in the CLLocation, if you just compare newLocation to oldLocation, it is always unequal.
    /*if (newLocation.coordinate.latitude != oldLocation.coordinate.latitude &&
        newLocation.coordinate.longitude != oldLocation.coordinate.longitude) {*/
        
        //send the location to the server
        [[NetworkingCenter defaultServer] sendMessage:
         [NetworkMessageCreator locationMessageWithLatitude:newLocation.coordinate.latitude 
                                                  longitude:newLocation.coordinate.longitude 
                                                   accuracy:newLocation.horizontalAccuracy]
         ];
        //center the mapview on the current location of the player
        [self.mapView setCenterCoordinate:self.currentLocation.coordinate animated:YES];
   // }
}

-(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
    if (newHeading.headingAccuracy < 0)
        return;
    
    //use true heading if its value is valid
    CLLocationDirection tempHeading = ((newHeading.trueHeading >0)?newHeading.trueHeading:newHeading.magneticHeading);
    
    //update the heading
    heading = tempHeading;
    
    //set the transform of the map view to be such that it rotates according to the heading direction
    [mapView setTransform:CGAffineTransformMakeRotation(-1 * heading * 3.14159 / 180)];
    
    for (id<MKAnnotation> annotation in mapView.annotations) 
    {
        MKAnnotationView* annotationView = [mapView viewForAnnotation:annotation];
        [annotationView setTransform:CGAffineTransformMakeRotation(heading * 3.14159 / 180)];
    }
}                                            

-(void)handleZooming:(UIGestureRecognizer *)gestureRecognizer {
    if (gestureRecognizer.state != UIGestureRecognizerStateChanged) {
        return;
    }
    if(currentLocation != nil)
        [self.mapView setCenterCoordinate:currentLocation.coordinate animated:YES];
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

- (void)changePoints:(int)points 
{
    //make animation for fading the value out updating it and fading it back in
    [UIView animateWithDuration:1.0
                     animations:^{ 
                         //hide label to change its value
                         bar.pointAmount.alpha = 0.0;
                     } 
                     completion:^(BOOL finished){
                         
                         [UIView animateWithDuration:1.0
                                          animations:^{ 
                                              //add the aquired points to the current amount
                                              self.bar.currentPoints += points;
                                              
                                              //make visile again
                                              bar.pointAmount.alpha = 1.0;
                                          } 
                                          completion:^(BOOL finished){
                                              ;
                                          }];
                         
                     }];
}

- (IBAction)huntedDown:(id)sender 
{
    //create the red view
    redView = [[UIView alloc] init];
    
    //set its color, opacity and frame
    [redView setBackgroundColor:[UIColor redColor]];
    [redView setAlpha:0.0];
    [redView setFrame:CGRectMake(mapView.frame.origin.x , mapView.frame.origin.y + 50, mapView.frame.size.width, mapView.frame.size.height - 165)];
    
    //add it as a subview to the mapview by putting it just above it
    [self.view insertSubview:redView aboveSubview:self.view];
    
    
    //get the path of the alarm sound
    NSString *path = [NSString stringWithFormat:@"%@%@", [[NSBundle mainBundle]resourcePath], @"/beep.wav"];
    SystemSoundID soundID;
    
    //get url from the path
    NSURL *filePath = [NSURL fileURLWithPath:path isDirectory:NO];
    
    //create the sound using audio services
    AudioServicesCreateSystemSoundID((CFURLRef)filePath, &soundID);
    
    //play the alert sound
    AudioServicesPlayAlertSound(soundID);
    
    //make animation that repeats and autoreverses
    [UIView animateWithDuration:1.0
                          delay:0.0
                        options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse|UIViewAnimationOptionAllowUserInteraction
                     animations:^{ 
                         //play the alert sound
                         AudioServicesPlaySystemSound(soundID);
                         
                         //hide label to change its value
                         [redView setAlpha:0.4];
                     } 
                     completion:^(BOOL finished){
                         ;
                     }];
}

//call when rescued
- (IBAction)safe:(id)sender{
    [self.redView removeFromSuperview];
}

//method to respond to taps in the button inside the point capture view
-(void)collectOrCancel:(id)sender{
    //invert the state of the is capturing variable and depending on it update the label and button with appropriate text and activity indicator when collecting points
    checkpointCaptureView.isCapturing = !checkpointCaptureView.isCapturing;
    if (checkpointCaptureView.isCapturing) {
        [checkpointCaptureView.catchButton setTitle:@"Cancel" forState:UIControlStateNormal];
        [checkpointCaptureView.catchLabel setText:@"Picking points"];
        [checkpointCaptureView.activityView startAnimating];
        //self.timerForCPCapture = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(collected:) userInfo:nil repeats:NO];
        
        //inform server about starting to capture the CP
        [[NetworkingCenter defaultServer] sendMessage:[NetworkMessageCreator catchMessage]];
        
    }else{
        [checkpointCaptureView.catchButton setTitle:@"Collect" forState:UIControlStateNormal];
        [checkpointCaptureView.catchLabel setText:@"Points available"];
        [checkpointCaptureView.activityView stopAnimating];
        //[self.timerForCPCapture invalidate];
        
        //inform server about canceling the capture of CP
        [[NetworkingCenter defaultServer] sendMessage:[NetworkMessageCreator stopCatchMessage]];
    }
}

//called automatically when enough time elapsed that user was near the checkpoint
- (IBAction)collected:(id)sender{
    
    //remove the pin from the mapview
    [self.mapView removeAnnotation:self.availableCheckpoint];
    
    //update the points
    [self changePoints:[self.availableCheckpoint.points intValue]];
    
    //remove checkpoint
    self.availableCheckpoint = nil;
    
    //make animation that fades out the checkpoint capture view and releases it upon completion
    [UIView animateWithDuration:1.0
                          delay:0.0
                        options:UIViewAnimationOptionCurveEaseInOut
                     animations:^{ 
                         //hide label to change its value
                         [checkpointCaptureView setAlpha:0.0];
                     } 
                     completion:^(BOOL finished){
                         
                         //remove the capture view
                         [checkpointCaptureView removeFromSuperview];
                         checkpointCaptureView = nil;
                         
                         //display alert view to notify user about the hunter
                         UIAlertView *captureAlertView = [[UIAlertView alloc] initWithTitle:@"Congratilations" message:@"You captured the checkpoint" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
                         [captureAlertView show];
                         [captureAlertView release];
                     }];
}

- (void)startCapturingCP:(id)sender
{
    //create the view and add the target method for the button
    checkpointCaptureView = [[CheckpointCaptureView alloc] initWithFrame:CGRectMake(0, 410, 320, 50) sender:self];
    [self.view addSubview:checkpointCaptureView];
    //make animation that fades in the checkpoint capture view
    [UIView animateWithDuration:1.0
                          delay:0.0
                        options:UIViewAnimationOptionCurveEaseInOut
                     animations:^{ 
                         //hide label to change its value
                         [self.checkpointCaptureView setAlpha:0.75];
                     } 
                     completion:^(BOOL finished){
                         ;
                     }];
    
}
- (void)cancelCapturingCP:(id)sender
{
    //make animation that fades out the checkpoint capture view and releases it upon completion to disallow capturing
    [UIView animateWithDuration:1.0
                          delay:0.0
                        options:UIViewAnimationOptionCurveEaseInOut
                     animations:^{ 
                         //hide label to change its value
                         [checkpointCaptureView setAlpha:0.0];
                     } 
                     completion:^(BOOL finished){
                         [checkpointCaptureView removeFromSuperview];
                         checkpointCaptureView = nil;
                     }];
}

-(void) alertAboutNearbyHunter:(id)sender
{
    //display alert view to notify user about the hunter
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Warning!" message:@"There is a hunter nearby preparing to catch you" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
    
    [alertView show];
    [alertView release];
}

-(void) informAboutBeenCaught:(id)sender
{
    //display alert view to notify user about the hunter
    UIAlertView *caughtAlertView = [[UIAlertView alloc] initWithTitle:@"LOOOSER!" message:@"You were caught and lost all the points" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
    [caughtAlertView show];
    [caughtAlertView release];
    //make animation for fading the value out updating it and fading it back in
    [UIView animateWithDuration:1.0
                     animations:^{ 
                         //hide label to change its value
                         bar.pointAmount.alpha = 0.0;
                     } 
                     completion:^(BOOL finished){
                         
                         [UIView animateWithDuration:1.0
                                          animations:^{ 
                                              //add the aquired points to the current amount
                                              self.bar.currentPoints = 0;
                                              
                                              //make visile again
                                              bar.pointAmount.alpha = 1.0;
                                          } 
                                          completion:^(BOOL finished){
                                              ;
                                          }];
                         
                     }];
}

- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation {
    
    MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:@"myAnnotationID"];
    
    if (!annotationView) {
        annotationView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"myAnnotationID"] autorelease];
        annotationView.canShowCallout = YES;
    }else{
        annotationView.annotation = annotation;
    }
    
    if (annotation == mapView.userLocation) {
        [annotationView setImage:nil];
    }else{
        [annotationView setImage:[UIImage imageNamed:@"shrine.png"]];
    }
    
    return annotationView;
}

- (void) receiveNetworkingNotification:(NSNotification *) notification
{
    if (![[notification name] isEqualToString:NETWORKING_NOTIFICATION]) return;
    
    //get the network message
    NetworkMessage *networkMessage = (NetworkMessage*)[notification object];
    
    //check for the message received and call respective method
    switch (networkMessage.type) {
        case kAssignNotifyMessage:
        {
            NSLog(@"get CP from server yeeeeeey");
            NSMutableDictionary *cpData = (NSMutableDictionary*) networkMessage.data;
            if([[cpData valueForKey:@"assign"] boolValue]){
            double targetLatitude = [[cpData valueForKey:@"target_latitude"] doubleValue];
            double targetLongtitude = [[cpData valueForKey:@"target_longitude"] doubleValue];
            NSString *amountOfPoints = [[NSNumber numberWithInt:[[cpData valueForKey:@"target_points"] intValue]] stringValue];
            availableCheckpoint = [[CheckPoint alloc] initWithCoordinate:CLLocationCoordinate2DMake(targetLatitude,targetLongtitude) amountOfPoints:amountOfPoints];
            [self.mapView addAnnotation:self.availableCheckpoint];
            }
            else 
            {
                [self.mapView removeAnnotation:self.availableCheckpoint];
                self.availableCheckpoint = nil;
            }
        }
            break;
        case kAllowCatchMessage:
            self.availableCheckpoint.isAllowToCatch = YES;
            [self startCapturingCP:self];
            break;   
        case kDisallowCatchMessage:
            self.availableCheckpoint.isAllowToCatch = NO;
            [self cancelCapturingCP:self];
            break;
        case kCatchStartMessage:
            [self alertAboutNearbyHunter:self];
            break;
        case kBeingCaughtMessage:
            [self huntedDown:self];
            break;
        case kFreeMessage:
            [self safe:self];
            break;
        case kCatchEndMessage:
            [self collected:self];
            break;
        case kCaughtMessage:
            [self informAboutBeenCaught:self];
            break;
        case kKeyframeRHMessage:
        {
            NSLog(@"got key frame from server yey");
            //get the checkpoint from the message
            NSMutableDictionary *pData = (NSMutableDictionary*) networkMessage.data;
            if (isFirstMessageFromServer) {
                //get the current points
                self.bar.currentPoints = [[pData valueForKey:@"carrying_points"] intValue];
                //set to NO so point data is ignored after first time
                isFirstMessageFromServer = NO;
            }
            
            double targetLatitude = [[pData valueForKey:@"target_lat"] doubleValue];
            double targetLongtitude = [[pData valueForKey:@"target_long"] doubleValue];
            //Use the message to update the current status
            //Here is to check the case that, the runner has been assigned to a checkpoint, 
            //but he didn`t get the checkpoint in the client side.
            //PS:
            //Since in the keyframe, there is no a boolean for "assign", so we need to see whether the target is valid or not.
            if(targetLatitude!=0 && targetLongtitude!=0)
            {
                //Since the client may not get the kAssignNotifyMessage in a bad network situation/
                //We use the keyframe to make sure that the runner can get the checkpoint
                if(self.availableCheckpoint == nil){
                    //Lost the kAssignMessage,which assign value is YES.
                    NSString *amountOfPoints = [[NSNumber numberWithInt:[[pData valueForKey:@"target_points"] intValue]] stringValue];
                    availableCheckpoint = [[CheckPoint alloc] initWithCoordinate:CLLocationCoordinate2DMake(targetLatitude,targetLongtitude) amountOfPoints:amountOfPoints];
                    [self.mapView addAnnotation:self.availableCheckpoint];
                } else {
                    //To check whether the client is allowed, but the client didn`t receive the kAllowCatchMessage.
                    if([[pData valueForKey:@"allowed_to_catch"] boolValue] == YES){
                        //Lost the kAllowCatchMessage
                        //You have been allowed, but it mismatches here.
                        if(self.availableCheckpoint.isAllowToCatch == NO){
                            //Start to capture
                            self.availableCheckpoint.isAllowToCatch = YES;
                            [self startCapturingCP:self];
                        }
                    } else {
                        //Lost the kDisallowCatchMessage
                        //You are not allowed, but it mismatches here.
                        if(self.availableCheckpoint.isAllowToCatch == YES){
                            //Cancel capture here
                            self.availableCheckpoint.isAllowToCatch = NO;
                            [self cancelCapturingCP:self];
                        }
                    }
                }
            }else {
                if(self.availableCheckpoint != nil){
                    //Here we have two situations.
                    //First, the runner has been assigned, but he didn`t get the kAssignMessage.
                    //The other is the runner has been assigned and catching the checkpoint, but the client could not get the kCatchEndMessage.
                    
                    //To check whether the runner has lost the kCatchEndMessage
                    //The only way to compare these two situations is to see whether the checkpoint is the same.
                    if (self.bar.currentPoints != [[pData valueForKey:@"carrying_points"] intValue]) {
                        //Lost the kCatchEndMessage
                        [self collected:self];
                    }else{
                        //Lost the kAssignMessage,which assign value is NO.
                        [self.mapView removeAnnotation:self.availableCheckpoint];
                        self.availableCheckpoint = nil;
                    }
                }
            }
        }
            break;
        default:
            break;
    }        
}

@end
